package com.zhuoyian.dl.service;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;

import com.zhuoyian.dl.listener.DownloadListener;
import com.zhuoyian.dl.manager.DownloadManager;
import com.zhuoyian.dl.model.DLVO;
import com.zhuoyian.dl.util.DownloadLog;
import com.zhuoyian.dl.util.DownloadParameter;
import com.zhuoyian.dl.util.DownloadThreadFactory;
import com.zhuoyian.dl.util.TaskId;

public class DownloadPresent extends Present {
	/**
	 * 
	 */
	private final int block = 1024 * 512;
	/**
	 * �߳���
	 */
	private final int THREAD_NUM = 2;
	/**
	 * ���ش�С
	 */
	private long downloadSize = 0;
	/**
	 * ʣ�´�С
	 */
	private long leftoverSize = 0;
	/**
	 * �ļ���С
	 */
	private int fileSize = 0;
	/**
	 * ����
	 */
	private DownloadTask[] tasks;
	/**
	 * �ļ�
	 */
	private DownloadParameter parameter;

	/**
	 * handler
	 */
	private DownloadListener dlListener;
	/**
	 * �Ƿ�����
	 */
	private boolean isPoint;
	/**
	 * ��ʼ���ص�λ��
	 */
	private LinkedList<Integer> blocks;
	/**
	 * �Ƿ�ȡ������
	 */
	private boolean isCancel;

	private Object obj;

	private long time;

	public DownloadPresent(DownloadParameter parameter, Object obj,
			DownloadListener dlListener, boolean isPoint) {
		this.parameter = parameter;
		this.tasks = new DownloadTask[THREAD_NUM];
		this.dlListener = dlListener;
		this.isPoint = isPoint;
		this.blocks = new LinkedList<Integer>();
		this.isCancel = false;
		this.obj = obj;
		this.taskId = TaskId.get();
	}

	public DownloadParameter getDlFile() {
		return parameter;
	}

	public void setFileSize(int fileSize) {
		this.fileSize = fileSize;
		parameter.setTotalSize(fileSize);
		downloadSize = new File(parameter.getLocalPath()).length() - 1;
		if (downloadSize <= block * 2) {
			downloadSize = 0;
		} else {
			downloadSize = downloadSize - block * 2;
		}
		leftoverSize = fileSize - downloadSize + 1;
		int size = (int) (leftoverSize / block + 1);
		if (leftoverSize > 1) {
			for (int i = 0; i < size; i++) {
				blocks.addLast((int) (downloadSize + (block * i)));
			}
		}
	}

	/**
	 * 
	 * @param listener
	 * @return
	 * @throws Exception
	 */
	public DownloadPresent download(URL url) {
		if (downloadSize >= fileSize) {
			if (dlListener != null) {
				downListener(DownloadParameter.STATE_START, fileSize, 0.0f);
				downListener(DownloadParameter.STATE_DOWING, fileSize, 0.0f);
				downListener(DownloadParameter.STATE_COMPLETE, fileSize, 0.0f);
				DownloadManager.removePresent(taskId);
			}
			return this;
		}

		ArrayList<DLVO> dll = getDLModel();
		initThread(dll, url);
		// 开始下载
		downListener(DownloadParameter.STATE_START, (int) downloadSize, 0.0f);

		boolean notFinish = true;
		time = System.currentTimeMillis();
		while (notFinish) {
			long d = new File(parameter.getLocalPath()).length();
			float speed = (float)(downloadSize - d)/ (float)(time - System.currentTimeMillis());

			downloadSize = d;
			synchronized (this) {
				try {
					this.wait(800);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			iterationTask(url);

			downListener(DownloadParameter.STATE_DOWING, (int) downloadSize,
					speed);

			if (fileSize <= downloadSize) {
				notFinish = false;
			}

			/**
			 */
			if (isCancel) {
				for (int i = 0; i < this.tasks.length; i++) {
					if (tasks[i] != null) {
						tasks[i].setInterrupt(true);
					}
				}
				downListener(DownloadParameter.STATE_STOP, (int) downloadSize,
						speed);
				DownloadManager.removePresent(taskId);
				DownloadLog.print("Thread.interrupted()");

				return this;
			}
			time = System.currentTimeMillis();
		}

		downListener(DownloadParameter.STATE_COMPLETE, fileSize, 0.0f);
		DownloadManager.removePresent(taskId);
		return this;
	}

	private void downListener(int downState, int downSize, float speed) {
		if (dlListener != null) {
			parameter.setDownState(downState);
			parameter.setDownSize(downSize);
			parameter.setDownloadSpeed(speed);
			dlListener.onDownload(parameter, obj);
		}
	}

	/**
	 * �����߳�
	 * 
	 * @param index
	 * @param d
	 * @param url
	 * @param newTaskId
	 */
	private void newThread(int index, DLVO d, URL url, int newTaskId) {
		this.tasks[index] = new DownloadTask(d, url);
		DownloadThreadFactory.THREAD.execute(tasks[index]);
	}

	/**
	 * ��ʼ���ϵ�
	 * 
	 * @param dll
	 */
	private ArrayList<DLVO> getDLModel() {
		ArrayList<DLVO> dll = new ArrayList<DLVO>();
		DLVO dl = new DLVO();
		dl.setDownState(DownloadParameter.STATE_START);
		dl.setLocalPath(parameter.getLocalPath());
		dl.setName(parameter.getFileName());
		dl.setTotalSize(fileSize);
		dl.setUrl(parameter.getUrl());
		dl.setObj(obj);
		dll.add(dl);
		dll.add(dl.clone());
		return dll;
	}

	private void initThread(ArrayList<DLVO> dll, URL url) {
		int size = 1;
		if (blocks.size() > 1) {
			size = 2;
		}
		for (int i = 0; i < size; i++) {//
			DLVO d = dll.get(i);
			if (isPoint) {
				if (d.getStartSize() < 1) {
					d.setStartSize(blocks.removeFirst());
					d.setEndSize(d.getStartSize() + block + 1);
				}
			} else {
				d.setStartSize(blocks.removeFirst());
				d.setEndSize(d.getStartSize() + block + 1);
			}
			newThread(i, d, url, i + 1);
		}
	}

	private void iterationTask(URL url) {
		int size = 1;
		if (blocks.size() > 1) {
			size = 2;
		}

		for (int i = 0; i < size; i++) {
			if (this.tasks[i] != null && this.tasks[i].getDownLength() == -1) {//
				//
				newThread(i, tasks[i].getDlModel(), url, tasks[i].getDlModel()
						.getTaskId());
			} else if (this.tasks[i] != null && this.tasks[i].isFinish()
					&& blocks.size() != 0) {
				//
				int starSize = blocks.removeFirst();
				tasks[i].getDlModel().setStartSize(starSize);
				tasks[i].getDlModel().setEndSize(starSize + block + 1);
				tasks[i].getDlModel().setDownSize(0);
				newThread(i, tasks[i].getDlModel(), url, tasks[i].getDlModel().getTaskId());
			}
		}
	}

	@Override
	public void cancel() {
		isCancel = true;
	}

}
